// Copyright (C) 2024 Kumo inc.
// Author: Jeff.li lijippy@163.com
// All rights reserved.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published
// by the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <https://www.gnu.org/licenses/>.
//
#pragma once

#include <stdint.h>                            // uint64_t

#if defined(__cplusplus)

#include <turbo/log/logging.h>                      // KCHECK

#endif

typedef uint64_t kthread_t;

// tid returned by kthread_start_* never equals this value.
static const kthread_t INVALID_KTHREAD = 0;

// kthread tag default is 0
typedef int kthread_tag_t;
static const kthread_tag_t KTHREAD_TAG_INVALID = -1;
static const kthread_tag_t KTHREAD_TAG_DEFAULT = 0;

struct sockaddr;

typedef unsigned kthread_stacktype_t;
static const kthread_stacktype_t KTHREAD_STACKTYPE_UNKNOWN = 0;
static const kthread_stacktype_t KTHREAD_STACKTYPE_PTHREAD = 1;
static const kthread_stacktype_t KTHREAD_STACKTYPE_SMALL = 2;
static const kthread_stacktype_t KTHREAD_STACKTYPE_NORMAL = 3;
static const kthread_stacktype_t KTHREAD_STACKTYPE_LARGE = 4;

typedef unsigned kthread_attrflags_t;
static const kthread_attrflags_t KTHREAD_LOG_START_AND_FINISH = 8;
static const kthread_attrflags_t KTHREAD_LOG_CONTEXT_SWITCH = 16;
static const kthread_attrflags_t KTHREAD_NOSIGNAL = 32;
static const kthread_attrflags_t KTHREAD_NEVER_QUIT = 64;
static const kthread_attrflags_t KTHREAD_INHERIT_SPAN = 128;

// Key of thread-local data, created by kthread_key_create.
typedef struct {
    uint32_t index;    // index in KeyTable
    uint32_t version;  // ABA avoidance
} kthread_key_t;

static const kthread_key_t INVALID_KTHREAD_KEY = {0, 0};

#if defined(__cplusplus)

// Overload operators for kthread_key_t
inline bool operator==(kthread_key_t key1, kthread_key_t key2) {
    return key1.index == key2.index && key1.version == key2.version;
}

inline bool operator!=(kthread_key_t key1, kthread_key_t key2) { return !(key1 == key2); }

inline bool operator<(kthread_key_t key1, kthread_key_t key2) {
    return key1.index != key2.index ? (key1.index < key2.index) :
           (key1.version < key2.version);
}

inline bool operator>(kthread_key_t key1, kthread_key_t key2) { return key2 < key1; }

inline bool operator<=(kthread_key_t key1, kthread_key_t key2) { return !(key2 < key1); }

inline bool operator>=(kthread_key_t key1, kthread_key_t key2) { return !(key1 < key2); }

inline std::ostream &operator<<(std::ostream &os, kthread_key_t key) {
    return os << "kthread_key_t{index=" << key.index << " version="
              << key.version << '}';
}

#endif  // __cplusplus

typedef struct {
    pthread_rwlock_t rwlock;
    void *list;
    void *free_keytables;
    int destroyed;
} kthread_keytable_pool_t;

typedef struct {
    size_t nfree;
} kthread_keytable_pool_stat_t;

// Attributes for thread creation.
typedef struct kthread_attr_t {
    kthread_stacktype_t stack_type;
    kthread_attrflags_t flags;
    kthread_keytable_pool_t *keytable_pool;
    kthread_tag_t tag;

#if defined(__cplusplus)

    void operator=(unsigned stacktype_and_flags) {
        stack_type = (stacktype_and_flags & 7);
        flags = (stacktype_and_flags & ~(unsigned) 7u);
        keytable_pool = nullptr;
        tag = KTHREAD_TAG_INVALID;
    }

    kthread_attr_t operator|(unsigned other_flags) const {
        KCHECK(!(other_flags & 7)) << "flags=" << other_flags;
        kthread_attr_t tmp = *this;
        tmp.flags |= (other_flags & ~(unsigned) 7u);
        return tmp;
    }

#endif  // __cplusplus
} kthread_attr_t;

// kthreads started with this attribute will run on stack of worker pthread and
// all kthread functions that would block the kthread will block the pthread.
// The kthread will not allocate its own stack, simply occupying a little meta
// memory. This is required to run JNI code which checks layout of stack. The
// obvious drawback is that you need more worker pthreads when you have a lot
// of such kthreads.
static const kthread_attr_t KTHREAD_ATTR_PTHREAD =
        {KTHREAD_STACKTYPE_PTHREAD, 0, nullptr, KTHREAD_TAG_INVALID};

// kthreads created with following attributes will have different size of
// stacks. Default is KTHREAD_ATTR_NORMAL.
static const kthread_attr_t KTHREAD_ATTR_SMALL = {KTHREAD_STACKTYPE_SMALL, 0, nullptr,
                                                  KTHREAD_TAG_INVALID};
static const kthread_attr_t KTHREAD_ATTR_NORMAL = {KTHREAD_STACKTYPE_NORMAL, 0, nullptr,
                                                   KTHREAD_TAG_INVALID};
static const kthread_attr_t KTHREAD_ATTR_LARGE = {KTHREAD_STACKTYPE_LARGE, 0, nullptr,
                                                  KTHREAD_TAG_INVALID};

// kthreads created with this attribute will print log when it's started,
// context-switched, finished.
static const kthread_attr_t KTHREAD_ATTR_DEBUG = {
        KTHREAD_STACKTYPE_NORMAL, KTHREAD_LOG_START_AND_FINISH | KTHREAD_LOG_CONTEXT_SWITCH, nullptr,
        KTHREAD_TAG_INVALID};

static const size_t KTHREAD_EPOLL_THREAD_NUM = 1;
static const kthread_t KTHREAD_ATOMIC_INIT = 0;
static const size_t KTHREAD_TASK_EXECUTOR_THREAD_NUM = 8;

// Min/Max number of work pthreads.
static const int KTHREAD_MIN_CONCURRENCY = 3 + KTHREAD_EPOLL_THREAD_NUM;
static const int KTHREAD_MAX_CONCURRENCY = 1024;

typedef struct {
    void *impl;
    // following fields are part of previous impl. and not used right now.
    // Don't remove them to break ABI compatibility.
    unsigned head;
    unsigned size;
    unsigned conflict_head;
    unsigned conflict_size;
} kthread_list_t;

// TODO: kthread_contention_site_t should be put into kutex.
typedef struct {
    int64_t duration_ns;
    size_t sampling_range;
} kthread_contention_site_t;

typedef struct {
    unsigned *kutex;
    kthread_contention_site_t csite;
} kthread_mutex_t;

typedef struct {
} kthread_mutexattr_t;

typedef struct {
    kthread_mutex_t *m;
    int *seq;
} kthread_cond_t;

typedef struct {
} kthread_condattr_t;

typedef struct {
} kthread_rwlock_t;

typedef struct {
} kthread_rwlockattr_t;

typedef struct {
    unsigned int count;
} kthread_barrier_t;

typedef struct {
} kthread_barrierattr_t;

#if defined(__cplusplus)

class kthread_once_t;
namespace kthread {
    extern int kthread_once_impl(kthread_once_t *once_control, void (*init_routine)());
}

class kthread_once_t {
public:
    friend int kthread::kthread_once_impl(kthread_once_t *once_control, void (*init_routine)());

    enum State {
        UNINITIALIZED = 0,
        INPROGRESS,
        INITIALIZED,
    };

    kthread_once_t();

    ~kthread_once_t();

private:
    std::atomic<int> *_kutex;
};

#endif

typedef struct {
    uint64_t value;
} kthread_id_t;

// kthread_id returned by kthread_id_create* can never be this value.
// NOTE: don't confuse with INVALID_KTHREAD!
static const kthread_id_t INVALID_KTHREAD_ID = {0};

#if defined(__cplusplus)

// Overload operators for kthread_id_t
inline bool operator==(kthread_id_t id1, kthread_id_t id2) { return id1.value == id2.value; }

inline bool operator!=(kthread_id_t id1, kthread_id_t id2) { return !(id1 == id2); }

inline bool operator<(kthread_id_t id1, kthread_id_t id2) { return id1.value < id2.value; }

inline bool operator>(kthread_id_t id1, kthread_id_t id2) { return id2 < id1; }

inline bool operator<=(kthread_id_t id1, kthread_id_t id2) { return !(id2 < id1); }

inline bool operator>=(kthread_id_t id1, kthread_id_t id2) { return !(id1 < id2); }

inline std::ostream &operator<<(std::ostream &os, kthread_id_t id) { return os << id.value; }

#endif  // __cplusplus

typedef struct {
    void *impl;
    // following fields are part of previous impl. and not used right now.
    // Don't remove them to break ABI compatibility.
    unsigned head;
    unsigned size;
    unsigned conflict_head;
    unsigned conflict_size;
} kthread_id_list_t;

typedef uint64_t kthread_timer_t;
